package com.contest.quartz;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.alibaba.fastjson.JSON;
import com.contest.quartz.data.ScheduleJob;
import com.contest.quartz.data.ScheduleJobInfo;
import com.contest.quartz.factory.QuartzJobFactory;
import com.contest.quartz.factory.QuartzJobFactoryDisallowConcurrentExecution;
import com.contest.util.SpringContextHelper;
import org.apache.log4j.Logger;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;


/**
 * 
 *
 * @title: QuartzManager.java
 * @package cn.jeeweb.core.quartz
 * @description: 计划任务管理
 * @copyright: 2017 www.jeeweb.cn Inc. All rights reserved.
 *
 */
public class QuartzManager
{
	private final Logger log = Logger.getLogger(this.getClass());
	private SchedulerFactoryBean schedulerFactoryBean = SpringContextHelper.getBean(SchedulerFactoryBean.class);

	private volatile static QuartzManager qManager;
	private QuartzManager(){};
	public static QuartzManager getInstance()
	{
		if(qManager == null)
		{
			synchronized (QuartzManager.class)
			{
				if(qManager == null)
					qManager = new QuartzManager();
				return qManager;
			}
		}
		
		return qManager;
	}

	public static QuartzManager getInstance(ApplicationContext context)
	{
		if(qManager == null)
		{
			synchronized (QuartzManager.class)
			{
				if(qManager == null)
					qManager = new QuartzManager();
				if(qManager.schedulerFactoryBean == null)
					qManager.schedulerFactoryBean = context.getBean(SchedulerFactoryBean.class);
				return qManager;
			}
		}

		return qManager;
	}

	/**
	 * 添加任务
	 * 
	 * @param job
	 * @throws SchedulerException
	 */
	public void addJob(ScheduleJob job) throws Exception
	{

		System.out.println(schedulerFactoryBean.getScheduler().getMetaData().getThreadPoolSize());
		System.out.println(schedulerFactoryBean.getScheduler().getMetaData().getThreadPoolClass());
		if (job == null || !ScheduleJob.STATUS_RUNNING.equals(job.getJobStatus())) {
			return;
		}

		Class clazzObj = Class.forName(job.getBeanClass());
		if (!ScheduleJobInfo.class.isAssignableFrom(clazzObj))
		{
			throw new Exception("job must extends ScheduleJobInfo:" + clazzObj.getName());
		}

		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
		CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

		job.setTriggerType("cron");
		// 不存在，创建一个
		if (null == trigger)
		{
			Class<? extends Job> clazz = ScheduleJob.CONCURRENT_IS.equals(job.getIsConcurrent()) ? QuartzJobFactory.class : QuartzJobFactoryDisallowConcurrentExecution.class;

			JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();

			jobDetail.getJobDataMap().put("scheduleJob", job);
			System.out.println(JSON.toJSONString(job));

			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());

			trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build();

			scheduler.scheduleJob(jobDetail, trigger);
		} else
		{
			// Trigger已存在，那么更新相应的定时设置
			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());

			JobKey jobKey = trigger.getJobKey();
			//jobDetail为clone的对他的元素修改无法操作到真正的jobDetail这是个小坑
			JobDetail jobDetail = scheduler.getJobDetail(jobKey);
			ScheduleJob objJob = (ScheduleJob) jobDetail.getJobDataMap().get("scheduleJob");
			System.out.println(JSON.toJSONString(objJob));
			objJob.setCronExpression(job.getCronExpression());
			objJob.setDescription(job.getDescription());
			objJob.setBeanClass(job.getBeanClass());
			objJob.setMethodName(job.getMethodName());
			objJob.setSpringId(job.getSpringId());
			objJob.setDataMap(job.getDataMap());

			// 按新的cronExpression表达式重新构建trigger
			trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

			// 按新的trigger重新设置job执行
			scheduler.rescheduleJob(triggerKey, trigger);
			System.out.println(JSON.toJSONString(objJob));
		}
	}

	/**
	 * 添加任务
	 *
	 * @param job
	 * @throws SchedulerException
	 */
	public void addJob(ScheduleJob job , SimpleScheduleBuilder simpleScheduleBuilder) throws Exception {

		if (job == null || !ScheduleJob.STATUS_RUNNING.equals(job.getJobStatus())) {
			return;
		}

		Class clazzObj = Class.forName(job.getBeanClass());
		if (!ScheduleJobInfo.class.isAssignableFrom(clazzObj))
		{
			throw new Exception("job must extends ScheduleJobInfo:" + clazzObj.getName());
		}

		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());

		Trigger  trigger =  scheduler.getTrigger(triggerKey);

		job.setTriggerType("simple");
		// 不存在，创建一个
		if (null == trigger)
		{
			Class<? extends Job> clazz = ScheduleJob.CONCURRENT_IS.equals(job.getIsConcurrent()) ? QuartzJobFactory.class : QuartzJobFactoryDisallowConcurrentExecution.class;

			JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();

			jobDetail.getJobDataMap().put("scheduleJob", job);
			System.out.println(JSON.toJSONString(job));

			trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(simpleScheduleBuilder).build();
			scheduler.scheduleJob(jobDetail, trigger);

		}
	}

	/**
	 * 获取所有计划中的任务列表
	 * 
	 * @return
	 * @throws SchedulerException
	 */
	public List<ScheduleJob> getAllJob() throws SchedulerException
	{
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
		Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
		List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
		for (JobKey jobKey : jobKeys)
		{
			JobDetail jobDetail = scheduler.getJobDetail(jobKey);
			ScheduleJob objJob = (ScheduleJob) jobDetail.getJobDataMap().get("scheduleJob");
			jobList.add(objJob);
//			System.out.println(JSON.toJSONString(objJob));
//			List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
//			for (Trigger trigger : triggers)
//			{
//				ScheduleJob job = new ScheduleJob();
//				job.setJobName(jobKey.getName());
//				job.setJobGroup(jobKey.getGroup());
//				job.setDescription("触发器:" + trigger.getKey());
//				Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
//				job.setJobStatus(triggerState.name());
//				if (trigger instanceof CronTrigger)
//				{
//					CronTrigger cronTrigger = (CronTrigger) trigger;
//					String cronExpression = cronTrigger.getCronExpression();
//					job.setCronExpression(cronExpression);
//				}
//				jobList.add(job);
//			}
		}

		return jobList;
	}

	public JobDetail getJobDetailByJob(ScheduleJob job) throws SchedulerException {
		if(job == null)
			return  null;

		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
		Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
		List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
		for (JobKey jobKey : jobKeys)
		{
			JobDetail jobDetail = scheduler.getJobDetail(jobKey);
			ScheduleJob objJob = (ScheduleJob) jobDetail.getJobDataMap().get("scheduleJob");
			if(objJob.getJobGroup().equals(job.getJobGroup()) && objJob.getJobName().equals(job.getJobName()))
			{
				return  jobDetail;
			}
		}
		return null;
	}

	public ScheduleJob getJobDetailJobByJob(ScheduleJob job) throws SchedulerException {
		JobDetail jobDetail = getJobDetailByJob(job);
		if(jobDetail == null) return null;
		return (ScheduleJob) jobDetail.getJobDataMap().get("scheduleJob");
	}

	/**
	 * 所有正在运行的job
	 * 
	 * @return
	 * @throws SchedulerException
	 */
	public List<ScheduleJob> getRunningJob() throws SchedulerException
	{
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
		List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
		for (JobExecutionContext executingJob : executingJobs)
		{
			ScheduleJob job = new ScheduleJob();
			JobDetail jobDetail = executingJob.getJobDetail();
			JobKey jobKey = jobDetail.getKey();
			Trigger trigger = executingJob.getTrigger();
			job.setJobName(jobKey.getName());
			job.setJobGroup(jobKey.getGroup());
			job.setDescription("触发器:" + trigger.getKey());
			Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
			job.setJobStatus(triggerState.name());
			if (trigger instanceof CronTrigger)
			{
				CronTrigger cronTrigger = (CronTrigger) trigger;
				String cronExpression = cronTrigger.getCronExpression();
				job.setCronExpression(cronExpression);
			}
			jobList.add(job);
		}
		return jobList;
	}

	/**
	 * 暂停一个job
	 * 
	 * @param scheduleJob
	 * @throws SchedulerException
	 */
	public void pauseJob(ScheduleJob scheduleJob) throws SchedulerException
	{
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
		scheduler.pauseJob(jobKey);
	}

	/**
	 * 恢复一个job
	 * 
	 * @param scheduleJob
	 * @throws SchedulerException
	 */
	public void resumeJob(ScheduleJob scheduleJob) throws SchedulerException
	{
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
		scheduler.resumeJob(jobKey);
	}

	/**
	 * 删除一个job
	 * 
	 * @param scheduleJob
	 * @throws SchedulerException
	 */
	public void deleteJob(ScheduleJob scheduleJob) throws SchedulerException
	{
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
		scheduler.deleteJob(jobKey);

	}

	/**
	 * 立即执行job
	 * 
	 * @param scheduleJob
	 * @throws SchedulerException
	 */
	public void runAJobNow(ScheduleJob scheduleJob) throws SchedulerException
	{
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
		scheduler.triggerJob(jobKey);
	}

	/**
	 * 更新job时间表达式
	 * 
	 * @param scheduleJob
	 * @throws SchedulerException
	 */
	public void updateJobCron(ScheduleJob scheduleJob) throws Exception
	{
		addJob(scheduleJob);
	}
}